//c
#include <unistd.h>

//stream
#include <iostream>
#include <sstream>
#include <fstream>

//containers
#include <vector>
#include <queue>
#include <stack>
#include <list>
#include <array>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <set>
#include <string>

//others
#include <algorithm>
#include <numeric>
#include <bitset>
#include <random>

using namespace std;
class Solution{
public:
	string sortString(string& s){
		cout << "I am AC, user output:我是用户" << endl;
		sort(s.begin(), s.end());
		return s;
	}
};

#include "mGenJson.hpp"
#include "template_fillInputArgs.hpp"

static const string UserInputPos = "userinput.txt";
static const string RunInputPos = "runinput.txt";
static const string RunRealansPos = "runrealans.txt";
static const string RunRightansPos = "runrightans.txt";

static const string inputSubmitfilePos = "inputSubmit.txt";     //for miltiple testcases judge
static const string outputSubmitfilePos = "outputSubmit.txt";   //for miltiple testcases judge


static const int GBL_JSBC_RUNTEST_OK = 0x00;
static const int GBL_JSBC_RUNTEST_OVERFLOW = 0x01;  //special: return by addresssanitizer or node directedly when run usercode!
static const int GBL_JSBC_RUNTEST_ERROR_PARA = 0x02;
static const int GBL_JSBC_RUNTEST_INPUT_NOTFOUND = 0x03;
static const int GBL_JSBC_RUNTEST_INPUT_DATA_INVALID = 0x04;
static const int GBL_JSBC_RUNTEST_OUTPUT_CANNOT_SAVE = 0x05;
static const int GBL_JSBC_RUNTEST_WA = 0x06;
static const int GBL_JSBC_RUNTEST_DEBUG = 0x07;

/*
 * read data from file, 
 * input const std::string& fileName
 * return pair<bool, vector<string>>, if OK, first true, second filecontent; else first false, second ""
 */
pair<bool, vector<string>> read_file_to_string(std::string const& fileName)
{
	std::ifstream fin(fileName, std::ios::in);

    if (!fin.is_open()) {
		cout << "fileName:" << fileName << " not found" << endl;
        return {false, {}};
    } else {
		vector<string> res;
		char line[65536] = {0};
		while(fin.getline(line, sizeof(line)))
		{
			string temp = (string)line;
			if (!temp.empty() && temp.back() == 0x0D){
				temp.pop_back();
			}
			res.emplace_back(temp);
		}
		fin.clear();
		fin.close();
		return {true, res};
    }
}

/*
 * create file
 * input string const& fileName,
 * return bool, if OK, true; else false
 */
bool create_file_truncate(string const& fileName){
    std::fstream s;
	s.open(fileName, std::fstream::out|std::fstream::trunc);
    if (!s.is_open()) {
        return false;
    } else {
		s.close();
		return true;
    }
}

/*
 * write data to file
 * input string const& fileName, string const& content
 * return bool, if OK, true; else false
 */
bool write_string_to_file_append(string const& fileName, string const& content)
{
    std::fstream s;
	s.open(fileName, std::fstream::out|std::fstream::app);
    if (!s.is_open()) {
        return false;
    } else {
		s << content;
		s.close();
		return true;
    }
}

/*
 * for sort test
 * note: must input with exactedly two para, first shall be a timestamp type
 *    second must be a name with full directory, eq: testproblems/sort/
 * return code
 * const int GBL_JSBC_RUNTEST_OK = 0x00;
 * const int GBL_JSBC_RUNTEST_OVERFLOW = 0x01;  //special: return by addresssanitizer or node directedly when run usercode!
 * const int GBL_JSBC_RUNTEST_ERROR_PARA = 0x02;
 * const int GBL_JSBC_RUNTEST_INPUT_NOTFOUND = 0x03;
 * const int GBL_JSBC_RUNTEST_INPUT_DATA_INVALID = 0x04;
 * const int GBL_JSBC_RUNTEST_OUTPUT_CANNOT_SAVE = 0x05;
 * const int GBL_JSBC_RUNTEST_WA = 0x06;
 * const int GBL_JSBC_RUNTEST_DEBUG = 0x07;
 *        the other codes are not predifined, if have other normal exit with exitcode(program normal exit by inside fuction runtime error!, if occur, tell me), in common condition, YOU SHOULD NOT use these not predifined codes here, otherwise confusing question may occur!
 */
int main(int argc, char* argv[]) {
	if (argc != 3){
		return GBL_JSBC_RUNTEST_ERROR_PARA;
	}else{
		string testId(argv[1]);
		
		string directory = argv[2];
		
		string tempOut;
#ifndef submit
		//read data from input.txt, so many data
		pair<bool, vector<string>> readret = read_file_to_string(directory + testId + UserInputPos);
		if (!readret.first){
			return GBL_JSBC_RUNTEST_INPUT_NOTFOUND;
		}
		
		//clear openfile, write runinput first
		if (!create_file_truncate(directory + testId + RunInputPos)){
			return GBL_JSBC_RUNTEST_OUTPUT_CANNOT_SAVE;
		}
		if (!write_string_to_file_append(directory + testId + RunInputPos,
		    readret.second[0]
			)){
			return GBL_JSBC_RUNTEST_OUTPUT_CANNOT_SAVE;
		}
		
		//use JSONlib to decode fileret.second!
		string p1;
		pair<bool, string> ret = m_JSONFrame::fillInputArgs(readret.second, 0, &p1);

		if (!ret.first){
			cerr << ret.second << endl;
			return GBL_JSBC_RUNTEST_INPUT_DATA_INVALID; 
		}
		
		Solution s = Solution();
		string rawres = s.sortString(p1);
		
		//may use JSONlib to encode res!
		mGenJson m = mGenJson(rawres);
		string JSONres = m.getJSONString();
		
		//write output second
		if (!write_string_to_file_append(directory + testId + RunRealansPos, JSONres)){
			return GBL_JSBC_RUNTEST_OUTPUT_CANNOT_SAVE;
		}
		
		return GBL_JSBC_RUNTEST_OK;
#else
		//read data from inputSubmit/outputSubmit.txt
		pair<bool, vector<string>> readret = read_file_to_string(directory + inputSubmitfilePos);
		pair<bool, vector<string>> oureadret = read_file_to_string(directory + outputSubmitfilePos);
		if (!readret.first || !oureadret.first || (readret.second.size() != oureadret.second.size())){
			return GBL_JSBC_RUNTEST_INPUT_NOTFOUND;
		}
		
		for (int line = 0; line < readret.second.size(); ++line){
			//clear openfile, write runinput first
			if (!create_file_truncate(directory + testId + RunInputPos)){
				return GBL_JSBC_RUNTEST_OUTPUT_CANNOT_SAVE;
			}
			if (!write_string_to_file_append(directory + testId + RunInputPos,
				readret.second[line]
				)){
				return GBL_JSBC_RUNTEST_OUTPUT_CANNOT_SAVE;
			}
			
			//use JSONlib to decode readret.second!
			string p1;
			pair<bool, string> ret = m_JSONFrame::fillInputArgs(readret.second, line, &p1);
			
			if (!ret.first){
				cerr << ret.second << endl;
				return GBL_JSBC_RUNTEST_INPUT_DATA_INVALID;
			}
			
			Solution s = Solution();
			string rawres = s.sortString(p1);
			
			//may use JSONlib to encode res!
			mGenJson m = mGenJson(rawres);
			string JSONres = m.getJSONString();
			
			if (JSONres != oureadret.second[line]){
				//save runrealans and runrightans
				if (!write_string_to_file_append(directory + testId + RunRealansPos, JSONres)){
					return GBL_JSBC_RUNTEST_OUTPUT_CANNOT_SAVE;
				}
				if (!write_string_to_file_append(directory + testId + RunRightansPos, oureadret.second[line])){
					return GBL_JSBC_RUNTEST_OUTPUT_CANNOT_SAVE;
				}
				return GBL_JSBC_RUNTEST_WA;
			}
		}
		if (!create_file_truncate(directory + testId + RunInputPos)){
			return GBL_JSBC_RUNTEST_OUTPUT_CANNOT_SAVE;
		}
		return GBL_JSBC_RUNTEST_OK;
#endif
	}
}
